const Service = require('egg').Service
const { Op } = require('sequelize')
class BaseService extends Service {
	/**
	 * 获取全部 / 分页 /
	 * @param {*} options   // 拼接好的where 查询条件
	 * @param {*} associateMode // 同步查询 关联的模型
	 * @param {*} associateModalWhere  // 对关联模型搜索的条件
	 * @param {*} orderKey // 对关联模型的排序字段
	 * @returns
	 */
	async list(
		options,
		associateMode = null,
		associateModalWhere = null,
		orderKey = null
	) {
		// 统一全部使用findAndCountAll
		// 存在关联模型 并且选择了要将关联数据一起查出来
		if (associateMode?.associateModelConfig[3]) {
			let other = {} // 拼接关联模型的where搜索条件
			if (associateModalWhere) {
				other['where'] = {
					[associateModalWhere.associateKey]: {
						[Op.eq]: [associateModalWhere.associateVal],
					},
				}
			}
			// 拼接关联模型排序
			if (orderKey) {
				orderKey = [[`${[associateMode.associateModelAs]}`, orderKey, 'ASC']]
			}
			// 拼接所有的查询条件
			let where = {
				include: [
					{
						model: this.ctx.model[associateMode.associateModelOtherModel],
						as: `${[associateMode.associateModelAs]}`,
						through: {
							attributes: [],
						},
						...other,
					},
				],
				order: orderKey,
			}
			return await this.ctx.model[this.modelName].findAndCountAll({
				...options,
				...where,
			})
		} else {
			// 不存在关联模型直接按照options查询
			return await this.ctx.model[this.modelName].findAndCountAll(options)
		}
	}

	/**
	 * 通过id获取详情页面
	 * @param {*} id 要查询的 id
	 * @returns
	 */
	async getInfoById(id) {
		let data = await this.ctx.model[this.modelName].findOne({
			where: {
				id: {
					[Op.eq]: id,
				},
			},
		})
		if (data) return { flag: true, message: '查询成功', data }
		else return { flag: false, message: '查询失败' }
	}

	/**
	 *  更新单条数据功能
	 * @param {*} id // 要更新的字段
	 * @param {*} obj  // 要更新的值
	 * @param {*} uniqueWords  // 表中不允许重复的字段
	 * @param {*} associateMode // 同步关联更新的模型
	 * @returns
	 */
	async update(id, obj, uniqueWords = null, associateMode = null) {
		// 判断 id 是否有效
		const IsExistedVal = await this.IsExisted(this.modelName, 'id', id)
		//  id 无效 直接返回
		if (!IsExistedVal) return { flag: false, message: '数据不存在' }
		// 表中存在不允许重复的字段
		if (uniqueWords) {
			const validateResult = await this.uniqueWordsValiDate(
				uniqueWords,
				obj,
				id
			)
			// 传入的数据存在重复
			if (!validateResult.flag) {
				// 直接返回 不需要更新
				return { flag: true, message: validateResult.message }
			}
			// 传入的数据存不存在重复
			// 并且存在关联模型要一起更新
			// 存在关联模型数据 并且需要一起更新数据
			if (associateMode && associateMode.associateModelConfig[2]) {
				// 建立事物
				let transaction
				try {
					transaction = await this.ctx.model.transaction()
					// 更新主表的信息
					let res = await this.updateOne(
						this.modelName,
						obj,
						'id',
						id,
						transaction
					)
					// 跟新关联表的信息
					// 先查看关联表有没有符合更新的字段
					const IsExistedVal = await this.IsExisted(
						associateMode['associateModelName'],
						associateMode['associateModelForeignKey'],
						id
					)
					// 如果存在 先全部删除在添加
					if (IsExistedVal) {
						let res2 = await this.ctx.model[
							associateMode['associateModelName']
						].destroy({
							where: {
								[associateMode['associateModelForeignKey']]: {
									[Op.eq]: id,
								},
							},
							transaction,
						})
						if (!res2) {
							throw Error
						}
					}
					// 执行批量添加操作
					if (obj.associateIds) {
						for (let index = 0; index < obj.associateIds.length; index++) {
							const IsExistedVal = await this.IsExisted(
								associateMode['associateModelOtherModel'],
								'id',
								obj.associateIds[index]
							)
							// console.log(IsExistedVal);
							if (IsExistedVal) {
								let res3 = await this.ctx.model[
									associateMode['associateModelName']
								].create(
									{
										[associateMode['associateModelForeignKey']]: id,
										[associateMode['associateModelOtherKey']]:
											obj.associateIds[index],
									},
									{
										transaction,
									}
								)
								if (!res3) {
									throw Error
								}
							} else {
								throw Error
							}
						}
					}
					// 操作都成功 提交事务
					if (!res) {
						throw Error
					}
					await transaction.commit()
					return {
						flag: true,
						message: '更新成功',
					}
				} catch (error) {
					// 事物回滚
					console.log(error)
					await transaction.rollback()
					return {
						flag: false,
						message: '修改失败',
					}
				}
			} else {
				// 不存在关联模型数据  直接更新
				const data = await this.updateOne(this.modelName, obj, 'id', id)
				// console.log(data);
				if (data[0]) {
					return { flag: true, message: `更新成功` }
				} else {
					return { flag: false, message: '更新失败' }
				}
			}
		} else {
			// 表中不存在 不允许重复的字段 直接更新
			const data = await this.updateOne(this.modelName, obj, 'id', id)
			if (data[0]) {
				return { flag: true, message: `更新成功` }
			} else {
				return { flag: false, message: '更新失败' }
			}
		}
	}
	/**
	 * 删除 / 关联删除 / 批量删除
	 * @param {*} ids
	 * @param {*} model // 第一个数据关联模型
	 * @param {*} model2 // 第二个数据关联模型 例如 删除角色 如果角色可以删除，角色对应的角色菜单表中role_d的数据也要一起全部删除
	 */
	async delAll({ ids, model = null, model2 = null }) {
		// 关联表删除
		if (model?.associateModelName) {
			let transaction
			try {
				// 建立事务对象
				transaction = await this.ctx.model.transaction()
				//遍历数据源
				for (let i = 0; i < ids.length; i++) {
					const id = ids[i]
					// 关联表删除数据
					// 先找有没有关联数据
					let IsExisted = await this.IsExisted(
						model.associateModelName,
						model.associateModelForeignKey,
						id
					)
					let res2 = true
					// 存在关联数据
					if (IsExisted) {
						// 选择了要删除关联数据 执行删除操作
						if (model?.isDelAssociate) {
							let IsExistedId = await this.IsExisted(
								model.associateModelName,
								model.associateModelForeignKey,
								id
							)
							if (IsExistedId) {
								res2 = await this.destroyOne(
									model.associateModelName,
									model.associateModelForeignKey,
									id,
									transaction
								)
							}
						} else {
							// 存在关联数据
							// 选择了不要删除关联数据 不执行删除操作 直接返回
							return { flag: false, message: '删除数据存在关联不能删除' }
						}
					}
					// 主表删除数据
					let IsExistedId = await this.IsExisted(this.modelName, 'id', id)
					let res = true
					if (!IsExistedId) return { flag: false, message: '删除数据不存在' }
					res = await this.destroyOne(this.modelName, 'id', id, transaction)
					// console.log(res);
					// console.log(res2);
					if (!res2 || !res) {
						throw Error
					}
					// 若果 还有其他的模型数据 也要一并删除 主要是这对 多个模型关联的数据

					if (model2?.isDelAssociate) {
						// 先看有没有数据关联
						let IsExisted = await this.IsExisted(
							model2.associateModelName,
							model2.associateModelForeignKey,
							id
						)

						if (IsExisted) {
							let res3 = await this.destroyOne(
								model2.associateModelName,
								model2.associateModelForeignKey,
								id,
								transaction
							)
							if (!res3) {
								throw Error
							}
						}
					}
				}

				// 提交事务
				await transaction.commit()
				return {
					flag: true,
					message: '删除成功',
				}
			} catch (error) {
				console.log(error)

				await transaction.rollback()
				return {
					flag: false,
					message: '删除失败。',
				}
			}
		} else {
			// 普通删除 数据不存在关联
			let transaction
			try {
				transaction = await this.ctx.model.transaction()
				for (let i = 0; i < ids.length; i++) {
					const id = ids[i]
					let IsExistedId = await this.IsExisted(this.modelName, 'id', id)
					if (!IsExistedId) {
						return { flag: false, message: '删除数据不存在或已经被删除了' }
					}
					let res
					res = await this.destroyOne(this.modelName, 'id', id, transaction)
					//  只要存在一个删除失败 就全部数据回滚
					if (!res) {
						throw Error
					}
				}
				await transaction.commit()
				return {
					flag: true,
					message: '删除成功',
				}
			} catch (error) {
				await transaction.rollback()
				return {
					flag: false,
					message: '删除失败。',
				}
			}
		}
	}

	/**
	 * 添加
	 * @param {*} obj
	 * @param {*} uniqueWord
	 * @returns
	 */
	async create(obj, uniqueWords, model = null) {
		// 新建对象不能重复字段
		if (uniqueWords) {
			const validateResult = await this.uniqueWordsValiDate(uniqueWords, obj)
			if (!validateResult.flag) {
				return { flag: false, message: validateResult.message }
			}
			let createObj = null
			//如果存在关联 模型  被关联的表同时也要创建数据
			//  console.log(model);
			if (model && model.associateModelConfig[0]) {
				// 获取关联数据
				let associateIds = obj.associateIds
				if (associateIds) {
					let transaction
					try {
						// 建立事务对象
						transaction = await this.ctx.model.transaction()

						let createdObj = await this.ctx.model[this.modelName].create(obj, {
							transaction,
						})
						// 主表中创建失败 直接回滚
						if (!createdObj) {
							console.log('createdObj创建失败')
							throw Error
						}
						// 开始创建关联表数据
						for (let i = 0; i < associateIds.length; i++) {
							const id = associateIds[i]
							// 先找 出入的被关联的数据 存不存在
							const IsExisted = this.IsExisted(
								model.associateModelOtherModel,
								'id',
								id
							)
							model.associateModelOtherModel
							// 存在继续执行 创建
							let res
							if (IsExisted) {
								res = await this.ctx.model[model.associateModelName].create(
									{
										[model.associateModelForeignKey]: createdObj.dataValues.id,
										[model.associateModelOtherKey]: id,
									},
									{
										transaction,
									}
								)
							} else {
								// 不存在 直接 回滚
								console.log('res创建失败')
								return {
									flag: false,
									message: `${model.associateModelOtherModel}表中不存在id为${id}的${model.associateModelOtherModel}`,
								}
							}
							// 关联表创建失败 回滚
							if (!res) {
								console.log('关联表创建失败')
								throw Error
							}
						}
						// 提交事务
						await transaction.commit()
						return {
							flag: true,
							message: '创建成功',
						}
					} catch (error) {
						await transaction.rollback()
						console.log(error)
						return {
							flag: false,
							message: '创建失败',
						}
					}
				} else {
					return { flag: false, message: '创建失败' }
				}
			} else {
				//如果不存在关联 模型  直接创建数据
				createObj = await this.ctx.model[this.modelName].create(obj)
				if (createObj) {
					return { flag: true, message: `创建成功` }
				} else {
					return { flag: false, message: '创建失败' }
				}
			}
		}
	}

	/**
	 * 关键字唯一判断辅助函数
	 * @param {*} uniqueWords 需要是唯一的字段 的数组
	 * @param {*} obj 传入的修改对象的值
	 * @returns
	 */
	async uniqueWordsValiDate(uniqueWords, obj, id) {
		for (let index = 0; index < uniqueWords.length; index++) {
			const item = uniqueWords[index]
			const IsExisted = await this.ctx.model[this.modelName].findOne({
				where: {
					[item]: {
						[Op.eq]: obj[item],
					},
				},
			})

			if (IsExisted) {
				id ? id : obj.id
				if (~~IsExisted?.dataValues?.id !== ~~id) {
					// console.log (IsExisted);
					// console.log(~~IsExisted?.dataValues?.id);
					return {
						flag: false,
						message: `系统中已经存在${item}为'${obj[item]}'的关键字了`,
					}
				}
			}
		}
		return { flag: true }
	}
	/**
	 * 批量插入
	 */
	async bulkCreates(arr, uniqueWords, model) {
		let repeatData = []
		let messages = []
		for (let i = 0; i < arr.length; i++) {
			const item = arr[i]
			let { flag, message } = await this.create(item, uniqueWords, model)

			if (!flag) {
				repeatData.push(item)
				messages.push(message)
			}
		}
		if (repeatData.length == arr.length) {
			return {
				flag: false,
				data: { repeatData, messages, count: arr.length - repeatData.length },
				message: '数据导入失败，请注意返回查看信息',
			}
		} else if (repeatData.length > 0) {
			return {
				flag: true,
				data: { repeatData, messages, count: arr.length - repeatData.length },
				message: '存在错误数据',
			}
		} else {
			return {
				flag: true,
				data: { count: arr.length - repeatData.length },
				message: '导入成功',
			}
		}
	}

	/**
	 * 辅助函数  判断一个值是否存在
	 * @param {*} model  查找模型
	 * @param {*} columnName  // 表中的字段名称
	 * @param {*} value  // 要查找的值
	 * @returns
	 */
	async IsExisted(model, columnName, value) {
		let flag = false
		const IsExisted = await this.ctx.model[model].findOne({
			where: {
				[columnName]: {
					[Op.eq]: value,
				},
			},
		})
		if (IsExisted) flag = true

		return flag
	}
	/**
	 * 辅助函数  向表中修改一条记录
	 * @param {*} obj
	 * @param {*} columnName 更新的依据
	 * @param {*} value  更新的依据对应的值
	 * @param {*} transaction  事务
	 */
	async updateOne(model, obj, columnName, value, transaction = null) {
		const data = await this.ctx.model[model].update(
			{ ...obj },
			{
				where: {
					[columnName]: {
						[Op.eq]: value,
					},
				},
				transaction,
			}
		)
		return data
	}
	/**
 * 
 辅助函数  向表中删除一条记录
	* @param {*} model 模型名称 
	* @param {*} columnName 删除的依据
	* @param {*} value  删除的依据对应的值
	* @param {*} transaction  事务
 * @returns 
 */
	async destroyOne(model, columnName, value, transaction = null) {
		const data = await this.ctx.model[model].destroy({
			where: {
				[columnName]: {
					[Op.eq]: value,
				},
			},
			transaction,
		})

		return data
	}

	/**
 * 
 辅助函数  向表中创建一条记录
	* @param {*} obj 
	* @param {*} transaction  事务
 * @returns 
 */
	async createOne(model, obj, transaction = null) {
		return await this.ctx.model[model].create(obj, { transaction })
	}
}

module.exports = BaseService
